// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Data
{
    internal class DataFormaterFactory
    {
        public static IDataKeyFormater GetKeyFormater(DataFormat format)
        {
            switch (format)
            {
                case DataFormat.Json: return new JsonKeyFormater();
                case DataFormat.Text: return new TextKeyFormater();
                case DataFormat.Xml: return new XmlKeyFormater();
                default: throw new NotImplementedException();
            }
        }

        public static INodeReader GetDataReader(string raw, DataFormat format)
        {
            switch (format)
            {
                case DataFormat.Text: return TNodeReader.Parse(raw);
                case DataFormat.Xml: return XNodeReader.Parse(raw);
                case DataFormat.Json:
                {
                    return JsonProviderFactory.Create().GetNodeReader(raw);
                }
                default: throw new NotImplementedException();
            }
        }

        public static INodeWriter GetDataWriter(DataFormat format)
        {
            switch (format)
            {
                case DataFormat.Text: return new TNodeWriter();
                case DataFormat.Xml: return new XNodeWriter();
                case DataFormat.Json:
                {
                    return JsonProviderFactory.Create().GetNodeWriter();
                }
                default: throw new NotImplementedException();
            }
        }

        public static IDataReadFormater<object> GetReadFormater(
            string raw, DataFormat format, DataFormatSetting setting = null, bool preserveChanges = false)
        {
            return GetReadFormater<object>(raw, null, format, setting, preserveChanges);
        }

        public static IDataWriteFormater<object> GetWriteFormater(
            IDictionary<string, Type> properties,
            DataFormat format,
            DataFormatSetting setting)
        {
            var modelMeta = properties.Any()
                ? ModelFactoryFactory.Create(properties).GetAccessor().ModelMeta
                : null;

            return GetWriteFormater<object>(modelMeta, format, setting);
        }

        private static IDataReadFormater<TModel> GetReadFormater<TModel>(
            string raw,
            IModelMeta modelMeta,
            DataFormat format,
            DataFormatSetting setting,
            bool preserveChanges)
        {
            FormatUtils.CheckImportSetting(setting, modelMeta);

            var root = GetDataReader(raw, format);

            if (root.NodeType == DataNodeType.Array)
            {
                return new SimpleReadFormater<TModel>(root, modelMeta, setting ?? new DataFormatSetting());
            }

            throw new InvalidOperationException(Properties.Resource.INCORRECT_DATAFORMAT);
        }

        private static IDataWriteFormater<TModel> GetWriteFormater<TModel>(
            IModelMeta modelMeta,
            DataFormat format,
            DataFormatSetting setting = null,
            string modelName = null)
        {
            var writer = GetDataWriter(format);
            IEnumerable<string> ignoreProperties = new List<string>();

            writer = writer.WriteAsArray(modelName);

            if (typeof(PocoModelMeta).IsAssignableFrom(modelMeta.GetType()))
            {
                ignoreProperties = modelMeta.Properties
                    .Where(x =>
                    {
                        if (x is IPocoProperty property)
                        {
                            return (format == DataFormat.Json && property.IsJsonIgnore)
                                || (format == DataFormat.Xml && property.IsXmlIgnore);
                        }

                        return false;
                    })
                    .Select(x => x.Name);
            }

            return new SimpleWriteFormater<TModel>(writer, modelMeta, ignoreProperties);
        }
    }
}
